package base

import (
	"jvm/rtda"
	"jvm/rtda/heap"
	. "jvm/typedef"
)

type Inst interface {
	ReadOperands(reader *CodeReader)
	Execute(frame *rtda.Frame)
}

// ****************************************************************
// 无操作数指令
type NoOperandsInst struct {
}

func (self *NoOperandsInst) ReadOperands(reader *CodeReader) {

}

// ****************************************************************
// 操作数为U1的索引的指令
type U1IndexInst struct {
	Index U2
}

func (self *U1IndexInst) ReadOperands(reader *CodeReader) {
	self.Index = U2(reader.ReadU1())
}

// ****************************************************************
// 操作数为U2的索引的指令
type U2IndexInst struct {
	Index U2
}

func (self *U2IndexInst) ReadOperands(reader *CodeReader) {
	self.Index = reader.ReadU2()
}

// ****************************************************************
// 分支指令
type BranchInst struct {
	offset I2
}

func (self *BranchInst) ReadOperands(reader *CodeReader) {
	self.offset = reader.ReadI2()
}
func (self *BranchInst) SetBranchPC(frame *rtda.Frame) {
	pc := I8(frame.Thread().PC())
	offset := I8(self.offset)
	addr := pc + offset
	frame.SetNextPC(U4(addr))
}

// ****************************************************************
// 方法调用
func MethodInvoke(invokerFrame *rtda.Frame, method *heap.Method) {
	thread := invokerFrame.Thread()
	newFrame := thread.NewFrame(method)
	thread.PushFrame(newFrame)
	stack := invokerFrame.OPStack()
	localVars := newFrame.LocalVars()

	for i := (I4)(method.ArgSlotCount()) - 1; i >= 0; i-- {
		localVars.SetSlot(U2(i), stack.PopSlot())
	}
	//hack
	// if method.IsNative() {
	// 	if method.Name() == "registerNatives" {
	// 		thread.PopFrame()
	// 	} else {
	// 		//暂不支持本地方法
	// 		panic(fmt.Sprintf("native method: %v.%v%v\n",
	// 			method.Class().Name(), method.Name(), method.Descriptor()))
	// 	}
	// }
}

// ****************************************************************
// 类初始化
func InitClass(thread *rtda.Thread, class *heap.Class) {
	class.SetInitialization()
	initClass(thread, class)
	initSuperClass(thread, class)
}

// 将类的初始化方法压入虚拟机栈
func initClass(thread *rtda.Thread, class *heap.Class) {
	clinit := class.GetClinitMethod()
	if clinit != nil {
		// exec <clinit>
		newFrame := thread.NewFrame(clinit)
		thread.PushFrame(newFrame)
	}
}

// ****************************************************************
func initSuperClass(thread *rtda.Thread, class *heap.Class) {
	if class.IsInterface() {
		return
	}
	for c := class.SuperClass(); c != nil; c = c.SuperClass() {
		if !c.IsInitialization() {
			c.SetInitialization()
			initClass(thread, c)
		} else {
			return
		}
	}
}
